﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using HeritageAcademy.Models.DataModels;
using HeritageAcademy.Models.Loaders;

namespace HeritageAcademy.Models.ViewModels
{
    public class TestWizardPageViewModel : INotifyPropertyChanged
    {
        #region Parameters

        private const string ID_DELIMITER = "_";
        private string _source;
        private readonly TestResultsLoader _resultsLoader;
        bool _isCurrentPage;
        int _currentPageNumber = 1;
        string _currentPageId = String.Empty;
        string _currentTestId;
        
        #endregion

        #region Ctor
        
        public TestWizardPageViewModel(TestResultsLoader resultsLoader)
        {
            _resultsLoader = resultsLoader;
        }
        
        #endregion

        #region Properties

        public string CurrentTestId
        {
            get { return _currentTestId; }
            set 
            { 
                _currentTestId = value;
                OnPropertyChanged("CurrentTestId");
            }
        }

        /// <summary>
        /// Test page source path.
        /// </summary>
        public string Source
        {
            get { return _source; }
            set 
            { 
                _source = value;
                OnPropertyChanged("Source");
            }
        }

        /// <summary>
        /// Current page number in the list of all pages.
        /// </summary>
        public int Number
        {
            get { return _currentPageNumber; }
            set 
            { 
                _currentPageNumber = value;
                OnPropertyChanged("Number");
            }
        }

        /// <summary>
        /// Current page id. 
        /// The first record is sufficient to get the page id.
        /// </summary>
        public string Id
        {
            get
            {
                if (String.IsNullOrEmpty(_currentPageId))
                    _currentPageId = (SeparateString(AvailableAnswers[0].Tag, ID_DELIMITER))[0];
                return _currentPageId;
            }
        }
        
        #region AvailableAnswers

        /// <summary>
        /// Returns a read-only collection of all AnswerType that the user can select.
        /// </summary>
        public ReadOnlyCollection<AnswerViewModel> AvailableAnswers { get; private set; }

        public void GetAvailableAnswers(Control ctrl)
        {
            var list = new List<AnswerViewModel>();
            var sp = FindChild<StackPanel>(ctrl, "spAnswers");

            if (sp != null)
            {
                foreach (var item in sp.Children)
                {
                    if (item.GetType() == typeof(CheckBox))
                    {
                        list.Add(new AnswerViewModel{ Tag = ((CheckBox)item).Tag.ToString(), IsSelected = false});
                        ((CheckBox)item).Click += TestWizardPageViewModel_CheckBox_Click;
                    }
                    if (item.GetType() == typeof(RadioButton))
                    {
                        list.Add(new AnswerViewModel{ Tag = ((RadioButton)item).Tag.ToString(), IsSelected = false});
                        ((RadioButton)item).Click += TestWizardPageViewModel_RadioButton_Click;
                        ((RadioButton)item).Unchecked += TestWizardPageViewModel_RadioButton_Click;
                    }
                }
            }

            //list.Sort();
            AvailableAnswers = new ReadOnlyCollection<AnswerViewModel>(list);

            // Create initial results record
            CreateResultsRecord();
        }

        void CreateResultsRecord()
        {
            TestResult testResult = null;
            var testExists = false;
            var pageExists = false;
            
            // find the previously created record
            // and drop the status to "false" (not checked)
            foreach (var result in _resultsLoader.Model.Results.Where(result => result.Id == _currentTestId))
            {
                testExists = true;
                testResult = result;
                foreach (var page in result.Pages.Where(page => page.Id == Id))
                {
                    pageExists = true;
                    foreach (var answer in page.Answers)
                        answer.Status = "false";
                }
            }

            if (!testExists)
            {
                testResult = new TestResult {Id = _currentTestId};
            }

            if (!pageExists)
            {
                var pageResult = new PageResult {Id = Id, Time = ""};

                for (var i = 1; i <= AvailableAnswers.Count; i++)
                {
                    var ans = new Answer { Id = SeparateString(AvailableAnswers[i - 1].Tag, ID_DELIMITER)[1], Status = "false" };
                    pageResult.Answers.Add(ans);
                }

                testResult.Pages.Add(pageResult);
                if (!testExists)
                    _resultsLoader.Model.Results.Add(testResult);    
                _resultsLoader.Save();
            }
        }

        void UpdateResultsRecord(string pageid, string answerid, bool? isChecked)
        {
            foreach (var answer in _resultsLoader.Model.Results.Where(result => result.Id == _currentTestId)
                                                                                        .SelectMany(result => result.Pages.Where(page => page.Id == pageid)
                                                                                        .SelectMany(page => page.Answers.Where(answer => answer.Id == answerid))))
            {
                answer.Status = (isChecked == true) ? "true" : "false";
            }
            _resultsLoader.Save();
        }

        public void FixTime(string pageid, string time)
        {
            foreach (var page in _resultsLoader.Model.Results.Where(result => result.Id == _currentTestId)
                                                                                    .SelectMany(result => result.Pages.Where(page => page.Id == pageid)))
            {
                page.Time = time;
            }
            _resultsLoader.Save();
        }

        void TestWizardPageViewModel_CheckBox_Click(object sender, RoutedEventArgs e)
        {
            UpdateResultsRecord(Separate(sender)[0], Separate(sender)[1], ((CheckBox)sender).IsChecked);
        }

        void TestWizardPageViewModel_RadioButton_Click(object sender, RoutedEventArgs e)
        {
            UpdateResultsRecord(Separate(sender)[0], Separate(sender)[1], ((RadioButton)sender).IsChecked);
        }

        static string[] Separate(object sender)
        {
            return SeparateString(((Control)sender).Tag.ToString(), ID_DELIMITER);
        }

        #endregion

        public bool IsCurrentPage
        {
            get { return _isCurrentPage; }
            set
            {
                if (value == _isCurrentPage)
                    return;

                _isCurrentPage = value;
                OnPropertyChanged("IsCurrentPage");
            }
        }

        #endregion

        #region Public Methods
        
        /// <summary>
        /// Returns true if the user has filled in this page properly
        /// and the wizard should allow the user to progress to the 
        /// next page in the workflow.
        /// </summary>
        public bool IsValid()
        {
            return !String.IsNullOrEmpty(_currentPageId);
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Convert (separate) input string into test and answer id's.
        /// </summary>
        /// <param name="inputString">Input string</param>
        /// <param name="delimiter">Delimiter</param>
        /// <returns>string[0] - test id; string[1] - answer id</returns>
        public static string[] SeparateString(string inputString, string delimiter)
        {
            var separator = Convert.ToChar(delimiter);
            return inputString.Split(separator);
        }

        /// <summary>
        /// Finds a Child of a given item in the visual tree. 
        /// </summary>
        /// <param name="parent">A direct parent of the queried item.</param>
        /// <typeparam name="T">The type of the queried item.</typeparam>
        /// <param name="childName">x:Name or Name of child. </param>
        /// <returns>The first parent item that matches the submitted type parameter. 
        /// If not matching item can be found, a null parent is being returned.</returns>
        public static T FindChild<T>(DependencyObject parent, string childName)
            where T : DependencyObject
        {
            // Confirm parent and childName are valid. 
            if (parent == null) return null;

            T foundChild = null;

            var childrenCount = VisualTreeHelper.GetChildrenCount(parent);
            for (var i = 0; i < childrenCount; i++)
            {
                var child = VisualTreeHelper.GetChild(parent, i);
                // If the child is not of the request child type child
                var childType = child as T;
                if (childType == null)
                {
                    // recursively drill down the tree
                    foundChild = FindChild<T>(child, childName);

                    // If the child is found, break so we do not overwrite the found child. 
                    if (foundChild != null) break;
                }
                else if (!string.IsNullOrEmpty(childName))
                {
                    var frameworkElement = (FrameworkElement)child;
                    // If the child's name is set for search
                    if (frameworkElement != null && frameworkElement.Name == childName)
                    {
                        // if the child's name is of the request name
                        foundChild = (T)child;
                        break;
                    }
                }
                else
                {
                    // child element found.
                    foundChild = (T)child;
                    break;
                }
            }

            return foundChild;
        }

        #endregion

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion
    }
}
